#include "Chunk/ChunkManager.h"
#include <queue>
#include <mutex>
#include <Thread/ThreadPool.hpp>
#include <MCWorld.h>

std::mutex ChunksMapMutex;

ChunkManager::ChunkManager()
{
    
}

ChunkManager::~ChunkManager()
{

}




#define ManhattanDistance(x1, y1, x2, y2) abs(x1-x2) + abs(y1-y2)


void ChunkManager::GenerateChunks(glm::vec2 PlayerPos)
{
   
    
    queue<glm::vec2> chunksQueue;

    int nowx = PlayerPos.x/16;
    if(PlayerPos.x<0) nowx -= 1;
    int nowy = PlayerPos.y/16;
    if(PlayerPos.y<0) nowy -= 1;
    chunksQueue.push(glm::vec2(nowx, nowy));
    uint32_t r = 0;

    while(!chunksQueue.empty())
    {
        auto now = chunksQueue.front();
        chunksQueue.pop();
        r = ManhattanDistance(now.x, now.y, nowx, nowy);

    
        if(this->ChunksMap.find(now) == this->ChunksMap.end())
        {
            auto c = new Chunk(this,now.x, now.y);
            c->GenerateChunk();
            this->ChunksMap[now] = c;
 
        }
      
        
        auto F = [&](int dx, int dy)
        {
            uint32_t newr =  ManhattanDistance(now.x+dx, now.y+dy, nowx, nowy);
            if(newr > r&& newr < MCWorld::GetGenerateRange())
                chunksQueue.push(glm::vec2(now.x+dx, now.y+dy));
        };

        F(-1, 0);
        F(1, 0);
        F(0, 1);
        F(0, -1);
        F(-1, -1);
        F(1, 1);
        F(1, -1);
        F(-1, 1);
    }
}


BlockBase * ChunkManager::GetBlock(glm::ivec3 pos)
{
    auto blockId = Chunk::GetGhunkID(glm::ivec2(pos.x, pos.y));
    if(this->ChunksMap.find(blockId) != this->ChunksMap.end())
    {
        auto ChunkBlockPos = Chunk::GetGhunkBlockPos(pos);

        return this->ChunksMap[blockId]->GetBlock(ChunkBlockPos.x, ChunkBlockPos.y, pos.z);//0~15 0~15 0~255
    }
        return nullptr;
}
void ChunkManager::RenderChunks(Blockshader &shader, Camera &camera)
{
    shader.use();
    shader.SetView(camera.GetViewMatrix());

    for(auto it = this->ChunksMap.begin(); it != this->ChunksMap.end(); it++)
    {
        if(ManhattanDistance(it->first.x, it->first.y, camera.position.x/16, camera.position.y/16) 
            > MCWorld::GetObservationRange())
        {
            continue;
        }
        it->second->Render(shader);
    }
    shader.Unuse();
}

Chunk * ChunkManager::GetChunk(int x, int y)
{   
    if(this->ChunksMap.find(glm::vec2(x, y)) != this->ChunksMap.end())
    {
        return this->ChunksMap[glm::vec2(x, y)];
    }
    return nullptr;
}